﻿/// <reference group = "Dedicated Worker" />
/// <reference path = "/lib/underscore-min.js" />
/// <reference path = "//Microsoft.WinJS.1.0/js/base.js" />
importScripts("//Microsoft.WinJS.1.0/js/base.js", "/lib/underscore.js");

var dbPath = Windows.Storage.ApplicationData.current.localFolder.path + '\\db.sqlite';
var currentVersion = 1;

function dbInit() {

    var request = indexedDB.open("assets", currentVersion);
    var objectStore = null;

    request.onsuccess = function (event) {
        dbOpened(request.result);
    }

    request.onupgradeneeded = function (event) {

        getPriceHistoryStore(request.result);
        getAssetInfoStore(request.result);
    }
}

function getPriceHistoryStore(db, access) {
    access = access || "readonly";
    if (_.contains(db.objectStoreNames, "price_history")) {
        return db.transaction(["price_history"], access).objectStore("price_history");
    }
    else {
        var objectStore = db.createObjectStore("price_history", { keyPath: "id", autoIncrement: true });
        objectStore.createIndex("symbol", "symbol", { unique: false });
        objectStore.createIndex("date", "date", { unique: false });

        return objectStore;
    }
}

function getAssetInfoStore(db, access) {
    access = access || "readonly";
    if (_.contains(db.objectStoreNames, "asset_info")) {
        return db.transaction(["asset_info"], access).objectStore("asset_info");
    } else {
        var objectStore = db.createObjectStore("asset_info", { keyPath: "symbol" });
        objectStore.createIndex("symbol", "symbol", { unique: false });

        return objectStore;
    }
}

function dbOpened(db) {

    var AssetPriceItem = WinJS.Class.define(
        function (values, symbol) {
            this.date = new Date(values[0]);
            this.symbol = symbol;
            this.openPrice = parseFloat(values[1]);
            this.highPrice = parseFloat(values[2]);
            this.lowPrice = parseFloat(values[3]);
            this.closePrice = parseFloat(values[4]);
            this.volume = parseFloat(values[5]);
            this.adjustedClosePrice = parseFloat(values[6]);
        }
    );

    var AssetInfo = WinJS.Class.define(
        function(symbol) {
            this.symbol = symbol;
        },
        {
            oldestCheck: new Date(),
            latestCheck: new Date()
        }
    );

    var AssetManager = WinJS.Class.define(
        function (symbol, startdate, oncomplete) {
            this.symbol = symbol;
            this.startdate = startdate;
            this.oncomplete = oncomplete;
        },
        {
            getAssets:
                function (db) {
                    requestAssets(this.db, this.symbol, this.startdate, this.oncomplete);
                }
        });

    function getAssetInfo(symbol) {
        return new WinJS.Promise(function(complete, error) {
            var request = getAssetInfoStore(db, "readonly").get(symbol);

            request.onsuccess = function(event) {
                
                var result = request.result;

                if(result) {
                    complete(result);
                } else {
                    var asset = new AssetInfo(symbol);
                    complete(asset);
                }
            };
        });
    }

    function putAssetInfo(asset) {
        return new WinJS.Promise(function(complete, error) {
            var request = getAssetInfoStore(db, "readwrite").put(asset);

            request.onsuccess = function(event) {
                var result = request.result;

                if(result) {
                    complete(result);
                } else {
                    error();
                }
            };
        });
    }

    function setOldestCheck(symbol, date) {
        return new WinJS.Promise(function (complete) {
            getAssetInfo(symbol).then(function (asset) {
                asset.oldestCheck = date;
                return putAssetInfo(asset);
            }).done(function(asset) {
                complete();
            });
        });
    }

    function setLatestCheck(symbol, date) {
        return new WinJS.Promise(function (complete) {
            getAssetInfo(symbol).then(function (asset) {
                asset.latestCheck = date;
                return putAssetInfo(asset);
            }).done(function(asset) {
                complete();
            });
        });
    }

    function fromYahoo(symbol, startDate, endDate) {
        var uri = getUri(symbol, startDate, endDate);

        var request = new XMLHttpRequest();

        request.open("GET", uri, false);
        request.send();

        var lines = request.responseText.split('\n');

        var myStocks = new Array();

        for (var i = lines.length - 1; i > 0; i--) {
            var values = lines[i].split(',');

            if (values.length == 7) {
                var stock = new AssetPriceItem(values, symbol);
                myStocks[myStocks.length] = stock;
            }
        }

        return myStocks;
    }

    function getUri(symbol, startDate, endDate) {
        
        var uri = "http://ichart.finance.yahoo.com/table.csv";
        uri += "?s=" + symbol;

        if (startDate) {
            var start = new Date(startDate);
            uri += "&a=" + start.getUTCMonth();
            uri += "&b=" + start.getUTCDate();
            uri += "&c=" + start.getUTCFullYear();
        }

        if (endDate) {
            var end = new Date(endDate);
            uri += "&d=" + end.getUTCMonth();
            uri += "&e=" + end.getUTCDate();
            uri += "&f=" + end.getUTCFullYear();
        }

        uri += "&g=d";

        return uri;
    }

    function getSymbolIndex(db, readtype){
        return getPriceHistoryStore(db, readtype).index("symbol");
    }

    function getSymbolCursor(db, symbol, readtype) {
        var request = getSymbolIndex(db, readtype).openCursor(IDBKeyRange.only(symbol));

        return request
    }

    function requestAssets(db, symbol, startDate, endDate) {

        return new WinJS.Promise(function (complete, error) {

            var cursor = getSymbolCursor(db, symbol, "readwrite");
            var assets = [];

            cursor.onsuccess =
                function (event) {
                    var cursor = event.target.result;

                    if (cursor) {
                        var asset = cursor.value;
                        if (asset.date >= startDate && asset.date <= endDate) {
                            assets.push(cursor.value);
                        }
                        cursor.continue();
                    }
                    else {
                        complete({ objectStore: event.target.source.objectStore, assets: assets });
                        //updateRequest(event.target.source.objectStore, symbol, startDate, endDate, assets);
                    }
                }
        });
    }

    function datesEqual(one, two) {
        return one.getDate() == two.getDate() && one.getMonth() == two.getMonth() && one.getFullYear() == two.getFullYear();
    }

    function datesWithinOneDay(one, two) {

    }

    function addToObjectStore(objectStore, assets) {
        assets.forEach(function (asset) {
            objectStore.add(asset);
        });
    }

    function updatePriceHistoryStore(db, symbol, startingDate, endingDate, existingAssets) {

        var objectStore = getPriceHistoryStore(db, "readwrite");

        return new WinJS.Promise(function (complete) {
            getAssetInfo(symbol).done(
                function (asset) {
                    var firstDate = asset.oldestCheck;
                    var lastDate = asset.latestCheck;

                    if (!firstDate) {
                        var stocks = fromYahoo(symbol, startingDate, endingDate);
                        addToObjectStore(objectStore, stocks);
                    }
                    else {
                        var prependStocks = null;
                        var postpendStocks = null;

                        if (firstDate > startingDate) {
                            var preEndDate = new Date(firstDate);
                            preEndDate.setUTCDate(preEndDate.getUTCDate() - 1);

                            prependStocks = fromYahoo(symbol, startingDate, preEndDate);
                            addToObjectStore(objectStore, prependStocks);
                            existingAssets.concat(prependStocks);

                            setOldestCheck(symbol, startingDate);
                        }

                        if (endingDate > lastDate) {
                            var postStartDate = new Date(lastDate);
                            postStartDate.setUTCDate(postStartDate.getUTCDate() + 1);

                            postpendStocks = fromYahoo(symbol, postStartDate, endingDate);
                            addToObjectStore(objectStore, postpendStocks);
                            existingAssets.concat(postpendStocks);

                            setLatestCheck(symbol, endingDate);
                        }
                    }

                    complete( _.sortBy(existingAssets, function (asset) {
                        return Number(asset.date);
                    }));
                });
        });
    }

    function getFirstDate(assets) {
        var firstDate = null;
        var firstAsset = _.min(assets, function (asset) { return asset.date; });

        if (firstAsset) {
            firstDate = firstAsset.date;
        }

        return firstDate;
    }

    function getLastDate(assets) {
        var lastDate = null;
        var lastAsset = _.max(assets, function (asset) { return asset.date; });

        if (lastAsset) {
            lastDate = lastAsset.date;
        }

        return lastDate;
    }

    function sendStocks(symbol, startDate, assets) {
        postMessage(
            {
                type: "stock request",
                symbol: symbol,
                date: startDate,
                stocks: assets
            });
    }

    onmessage = function (event) {
        var data = event.data;
        if (data.type == "stock request") {

            var startDate = data.date;
            var endDate = new Date();
            var symbol = data.symbol;

            requestAssets(db, symbol, startDate, endDate).then(
                function (results) {
                    return updatePriceHistoryStore(db, symbol, startDate, endDate, results.assets);
                }).done(
                function (finalAssets) {
                    sendStocks(symbol, startDate, finalAssets);
                });
        }
    }
}

dbInit();